package com.example.service;

import com.example.annotation.AuditLog;
import com.example.config.RateLimitService;
import com.example.exception.BusinessException;
import com.example.model.LaundryOrder;
import com.example.model.Payment;
import com.example.model.User;
import com.example.repository.LaundryOrderRepository;
import com.example.repository.PaymentRepository;
import com.example.repository.UserRepository;
import com.example.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 安全支付服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PaymentService {

    private final PaymentRepository paymentRepository;
    private final LaundryOrderRepository orderRepository;
    private final UserRepository userRepository;
    private final SecurityUtils securityUtils;
    private final RateLimitService rateLimitService;
    private final AuditLogService auditLogService;

    @Value("${payment.alipay.app-id:}")
    private String alipayAppId;

    @Value("${payment.alipay.private-key:}")
    private String alipayPrivateKey;

    @Value("${payment.wechat.app-id:}")
    private String wechatAppId;

    @Value("${payment.wechat.mch-id:}")
    private String wechatMchId;

    @Value("${payment.wechat.key:}")
    private String wechatKey;

    @Value("${payment.timeout-minutes:30}")
    private int paymentTimeoutMinutes;

    @Value("${payment.daily-limit:10000}")
    private BigDecimal dailyPaymentLimit;

    @Value("${payment.single-limit:5000}")
    private BigDecimal singlePaymentLimit;

    /**
     * 创建支付订单
     */
    @Transactional
    @AuditLog(operation = com.example.model.AuditLog.OperationType.CREATE, 
              module = "PAYMENT", description = "创建支付订单", 
              riskLevel = com.example.model.AuditLog.RiskLevel.HIGH)
    public Payment createPayment(Long orderId, Payment.PaymentMethod paymentMethod, 
                                String clientIp, String userAgent) {
        
        // 获取当前用户
        String userId = getCurrentUserId();
        
        // 安全检查
        performSecurityChecks(userId, clientIp);
        
        // 获取订单信息
        LaundryOrder order = orderRepository.findById(orderId)
                .orElseThrow(() -> new BusinessException("ORDER_NOT_FOUND", "订单不存在"));
        
        // 验证订单状态
        validateOrderForPayment(order, userId);
        
        // 检查是否已有支付记录
        checkDuplicatePayment(orderId);
        
        // 验证支付金额
        BigDecimal paymentAmount = order.getFinalAmount();
        validatePaymentAmount(paymentAmount, userId);
        
        // 创建支付记录
        Payment payment = createPaymentRecord(order, paymentMethod, paymentAmount, clientIp, userAgent);
        
        // 根据支付方式处理
        switch (paymentMethod) {
            case ALIPAY:
                processAlipayPayment(payment);
                break;
            case WECHAT:
                processWechatPayment(payment);
                break;
            case BALANCE:
                processBalancePayment(payment);
                break;
            default:
                throw new BusinessException("UNSUPPORTED_PAYMENT_METHOD", "不支持的支付方式");
        }
        
        // 记录审计日志
        auditLogService.logSensitiveOperation(
            com.example.model.AuditLog.OperationType.CREATE,
            "PAYMENT",
            "创建支付订单",
            Map.of("orderId", orderId, "amount", paymentAmount, "method", paymentMethod)
        );
        
        return paymentRepository.save(payment);
    }

    /**
     * 安全检查
     */
    private void performSecurityChecks(String userId, String clientIp) {
        // 检查用户支付频率
        if (!rateLimitService.isAllowed("payment_user:" + userId, 10, 3600)) {
            throw new BusinessException("PAYMENT_RATE_LIMIT", "支付过于频繁，请稍后再试");
        }
        
        // 检查IP支付频率
        if (!rateLimitService.isAllowed("payment_ip:" + clientIp, 20, 3600)) {
            throw new BusinessException("PAYMENT_IP_LIMIT", "该IP支付过于频繁");
        }
        
        // 检查今日支付限额
        LocalDateTime startOfDay = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
        LocalDateTime endOfDay = startOfDay.plusDays(1);
        
        BigDecimal todayAmount = paymentRepository.sumUserPaymentAmountToday(userId, startOfDay, endOfDay);
        if (todayAmount.compareTo(dailyPaymentLimit) >= 0) {
            throw new BusinessException("DAILY_LIMIT_EXCEEDED", "今日支付金额已达上限");
        }
    }

    /**
     * 验证订单支付条件
     */
    private void validateOrderForPayment(LaundryOrder order, String userId) {
        // 验证订单所有者
        if (!order.getUser().getId().toString().equals(userId)) {
            throw new BusinessException("ORDER_ACCESS_DENIED", "无权限访问该订单");
        }
        
        // 验证订单状态
        if (order.getPaymentStatus() != LaundryOrder.PaymentStatus.UNPAID) {
            throw new BusinessException("ORDER_ALREADY_PAID", "订单已支付或状态异常");
        }
        
        // 验证订单是否过期
        if (order.getCreatedAt().plusHours(24).isBefore(LocalDateTime.now())) {
            throw new BusinessException("ORDER_EXPIRED", "订单已过期");
        }
    }

    /**
     * 检查重复支付
     */
    private void checkDuplicatePayment(Long orderId) {
        Long duplicateCount = paymentRepository.countDuplicatePayments(orderId, 0L);
        if (duplicateCount > 0) {
            throw new BusinessException("DUPLICATE_PAYMENT", "订单已有支付记录");
        }
    }

    /**
     * 验证支付金额
     */
    private void validatePaymentAmount(BigDecimal amount, String userId) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("INVALID_AMOUNT", "支付金额无效");
        }
        
        if (amount.compareTo(singlePaymentLimit) > 0) {
            throw new BusinessException("AMOUNT_LIMIT_EXCEEDED", "单笔支付金额超过限制");
        }
        
        // 检查金额精度（最多2位小数）
        if (amount.scale() > 2) {
            throw new BusinessException("INVALID_AMOUNT_PRECISION", "金额精度错误");
        }
    }

    /**
     * 创建支付记录
     */
    private Payment createPaymentRecord(LaundryOrder order, Payment.PaymentMethod paymentMethod,
                                      BigDecimal amount, String clientIp, String userAgent) {
        Payment payment = new Payment();
        payment.setPaymentNo(Payment.generatePaymentNo());
        payment.setOrderId(order.getId());
        payment.setUserId(order.getUser().getId().toString());
        payment.setMerchantId(order.getMerchantId());
        payment.setPaymentMethod(paymentMethod);
        payment.setAmount(amount);
        payment.setActualAmount(amount);
        payment.setSubject("洗护服务费用");
        payment.setDescription("订单号：" + order.getOrderNumber());
        payment.setClientIp(clientIp);
        payment.setUserAgent(userAgent);
        payment.setExpireTime(LocalDateTime.now().plusMinutes(paymentTimeoutMinutes));
        payment.setStatus(Payment.PaymentStatus.PENDING);
        
        return payment;
    }

    /**
     * 处理支付宝支付
     */
    private void processAlipayPayment(Payment payment) {
        try {
            // 这里应该调用支付宝SDK
            // 为了演示，我们模拟生成支付URL
            String paymentUrl = generateAlipayUrl(payment);
            payment.setPaymentUrl(paymentUrl);
            payment.setPaymentChannel(Payment.PaymentChannel.ALIPAY_APP);
            
            log.info("创建支付宝支付: paymentNo={}, amount={}", payment.getPaymentNo(), payment.getAmount());
            
        } catch (Exception e) {
            log.error("创建支付宝支付失败: {}", e.getMessage(), e);
            throw new BusinessException("ALIPAY_CREATE_FAILED", "创建支付宝支付失败");
        }
    }

    /**
     * 处理微信支付
     */
    private void processWechatPayment(Payment payment) {
        try {
            // 这里应该调用微信支付SDK
            // 为了演示，我们模拟生成支付URL
            String paymentUrl = generateWechatUrl(payment);
            payment.setPaymentUrl(paymentUrl);
            payment.setPaymentChannel(Payment.PaymentChannel.WECHAT_APP);
            
            log.info("创建微信支付: paymentNo={}, amount={}", payment.getPaymentNo(), payment.getAmount());
            
        } catch (Exception e) {
            log.error("创建微信支付失败: {}", e.getMessage(), e);
            throw new BusinessException("WECHAT_CREATE_FAILED", "创建微信支付失败");
        }
    }

    /**
     * 处理余额支付
     */
    @Transactional
    private void processBalancePayment(Payment payment) {
        try {
            // 获取用户信息
            User user = userRepository.findById(Long.valueOf(payment.getUserId()))
                    .orElseThrow(() -> new BusinessException("USER_NOT_FOUND", "用户不存在"));
            
            // 检查余额
            if (user.getBalance().compareTo(payment.getAmount()) < 0) {
                throw new BusinessException("INSUFFICIENT_BALANCE", "余额不足");
            }
            
            // 扣除余额
            user.setBalance(user.getBalance().subtract(payment.getAmount()));
            userRepository.save(user);
            
            // 更新支付状态
            payment.setStatus(Payment.PaymentStatus.SUCCESS);
            payment.setPaidAt(LocalDateTime.now());
            payment.setPaymentChannel(Payment.PaymentChannel.BALANCE_INTERNAL);
            
            // 更新订单状态
            updateOrderPaymentStatus(payment.getOrderId(), LaundryOrder.PaymentStatus.PAID);
            
            log.info("余额支付成功: paymentNo={}, amount={}", payment.getPaymentNo(), payment.getAmount());
            
        } catch (Exception e) {
            log.error("余额支付失败: {}", e.getMessage(), e);
            throw new BusinessException("BALANCE_PAYMENT_FAILED", "余额支付失败");
        }
    }

    /**
     * 支付回调处理
     */
    @Transactional
    @AuditLog(operation = com.example.model.AuditLog.OperationType.UPDATE, 
              module = "PAYMENT", description = "处理支付回调", 
              riskLevel = com.example.model.AuditLog.RiskLevel.HIGH)
    public void handlePaymentCallback(String paymentNo, String thirdPartyNo, 
                                    Payment.PaymentStatus status, String notifyContent) {
        
        Payment payment = paymentRepository.findByPaymentNo(paymentNo)
                .orElseThrow(() -> new BusinessException("PAYMENT_NOT_FOUND", "支付记录不存在"));
        
        // 验证回调签名（这里应该根据具体支付渠道验证）
        if (!verifyCallbackSignature(payment, notifyContent)) {
            log.error("支付回调签名验证失败: paymentNo={}", paymentNo);
            throw new BusinessException("INVALID_CALLBACK_SIGNATURE", "回调签名验证失败");
        }
        
        // 防止重复处理
        if (payment.getStatus().isFinished()) {
            log.warn("支付已完成，忽略重复回调: paymentNo={}", paymentNo);
            return;
        }
        
        // 更新支付状态
        payment.setThirdPartyNo(thirdPartyNo);
        payment.setStatus(status);
        payment.setNotifyContent(notifyContent);
        payment.setNotifyTime(LocalDateTime.now());
        
        if (status == Payment.PaymentStatus.SUCCESS) {
            payment.setPaidAt(LocalDateTime.now());
            // 更新订单状态
            updateOrderPaymentStatus(payment.getOrderId(), LaundryOrder.PaymentStatus.PAID);
        }
        
        paymentRepository.save(payment);
        
        log.info("支付回调处理完成: paymentNo={}, status={}", paymentNo, status);
    }

    /**
     * 更新订单支付状态
     */
    private void updateOrderPaymentStatus(Long orderId, LaundryOrder.PaymentStatus paymentStatus) {
        LaundryOrder order = orderRepository.findById(orderId)
                .orElseThrow(() -> new BusinessException("ORDER_NOT_FOUND", "订单不存在"));
        
        order.setPaymentStatus(paymentStatus);
        if (paymentStatus == LaundryOrder.PaymentStatus.PAID) {
            order.setPaidAt(LocalDateTime.now());
            order.setStatus(LaundryOrder.OrderStatus.CONFIRMED);
        }
        
        orderRepository.save(order);
    }

    /**
     * 获取当前用户ID
     */
    private String getCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            throw new BusinessException("AUTHENTICATION_REQUIRED", "需要登录");
        }
        return authentication.getName();
    }

    /**
     * 生成支付宝支付URL（模拟）
     */
    private String generateAlipayUrl(Payment payment) {
        // 实际实现中应该使用支付宝SDK
        return "https://openapi.alipay.com/gateway.do?payment_no=" + payment.getPaymentNo();
    }

    /**
     * 生成微信支付URL（模拟）
     */
    private String generateWechatUrl(Payment payment) {
        // 实际实现中应该使用微信支付SDK
        return "https://api.mch.weixin.qq.com/pay/unifiedorder?payment_no=" + payment.getPaymentNo();
    }

    /**
     * 验证回调签名
     */
    private boolean verifyCallbackSignature(Payment payment, String notifyContent) {
        try {
            // 这里应该根据具体支付渠道的签名算法验证
            // 为了演示，我们简单验证内容不为空
            return StringUtils.hasText(notifyContent);
        } catch (Exception e) {
            log.error("验证回调签名异常: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 查询支付记录
     */
    public Payment getPaymentByNo(String paymentNo) {
        return paymentRepository.findByPaymentNo(paymentNo)
                .orElseThrow(() -> new BusinessException("PAYMENT_NOT_FOUND", "支付记录不存在"));
    }

    /**
     * 查询用户支付记录
     */
    public Page<Payment> getUserPayments(String userId, Pageable pageable) {
        return paymentRepository.findByUserIdOrderByCreatedAtDesc(userId, pageable);
    }

    /**
     * 查询商家支付记录
     */
    public Page<Payment> getMerchantPayments(Long merchantId, Pageable pageable) {
        return paymentRepository.findByMerchantIdOrderByCreatedAtDesc(merchantId, pageable);
    }
}
